package org.smartcti.freeswitch.inbound.internal;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.smartcti.freeswitch.inbound.event.ChannelBridgeEvent;
import org.smartcti.freeswitch.inbound.event.ChannelCreateEvent;
import org.smartcti.freeswitch.inbound.event.ChannelDestroyEvent;
import org.smartcti.freeswitch.inbound.event.ChannelExecuteEvent;
import org.smartcti.freeswitch.inbound.event.ChannelHangupEvent;
import org.smartcti.freeswitch.inbound.event.ChannelOutgoingEvent;
import org.smartcti.freeswitch.inbound.event.ChannelProgressEvent;
import org.smartcti.freeswitch.inbound.event.ChannelState;
import org.smartcti.freeswitch.inbound.event.ChannelStateEvent;
import org.smartcti.freeswitch.inbound.event.ConferenceEvent;
import org.smartcti.freeswitch.inbound.event.DisconnectEvent;
import org.smartcti.freeswitch.inbound.event.HeartbeatEvent;
import org.smartcti.freeswitch.inbound.event.InboundEvent;
import org.smartcti.freeswitch.inbound.event.PresenceInEvent;
import org.smartcti.freeswitch.inbound.event.SIPRegisterEvent;
import org.smartcti.freeswitch.inbound.event.SIPUnregisterEvent;
import org.smartcti.freeswitch.util.internal.CustomerChannelStateConverter;
import org.smartcti.freeswitch.util.internal.CustomerDateConverter;

public class EventBuilderImpl implements EventBuilder {
	private final Map<String, Class> registeredEventClasses;
	private final Map<String,String> upperCasePropertyies ;
	private static final Logger logger  = LogManager.getLogger(EventBuilderImpl.class);
	
	

	public EventBuilderImpl() {
		registeredEventClasses = new HashMap<String, Class>();
		upperCasePropertyies = new HashMap<String,String>();
		registerBuiltinEventClasses();
		CustomerDateConverter dateConverter = new CustomerDateConverter (); 
        ConvertUtils.register(dateConverter,Date.class);   
        CustomerChannelStateConverter channelStateConverter = new CustomerChannelStateConverter();
        ConvertUtils.register(channelStateConverter,ChannelState.class);   
	}
	
	
	

	

	
	
	 public final void registerEventClass(final String eventName,final Class<? extends InboundEvent> clazz) throws IllegalArgumentException{
		 registeredEventClasses.put(eventName, clazz);
		 final PropertyDescriptor[] descs = PropertyUtils.getPropertyDescriptors(clazz);
	        for(final PropertyDescriptor desc:descs){
	        	final String propertyName = desc.getName();
	        	upperCasePropertyies.put(eventName+propertyName.toUpperCase(), propertyName);
	        }
	 }

	
	
	private void registerBuiltinEventClasses()
    {
		this.registerEventClass("HEARTBEAT",HeartbeatEvent.class);
		this.registerEventClass("CHANNEL_CREATE",ChannelCreateEvent.class);
		this.registerEventClass("CHANNEL_STATE",ChannelStateEvent.class);
		this.registerEventClass("CHANNEL_OUTGOING",ChannelOutgoingEvent.class);
		this.registerEventClass("CHANNEL_PROGRESS",ChannelProgressEvent.class);
		this.registerEventClass("CHANNEL_BRIDGE",ChannelBridgeEvent.class);
		this.registerEventClass("CHANNEL_EXECUTE",ChannelExecuteEvent.class);
		this.registerEventClass("CHANNEL_HANGUP",ChannelHangupEvent.class);
		this.registerEventClass("CHANNEL_DESTROY",ChannelDestroyEvent.class);
		this.registerEventClass("CUSTOM:conference::maintenance",ConferenceEvent.class);
		this.registerEventClass("CUSTOM:sofia::unregister",SIPUnregisterEvent.class);
		this.registerEventClass("CUSTOM:sofia::register",SIPRegisterEvent.class);
		this.registerEventClass("SHUTDOWN",DisconnectEvent.class);
		this.registerEventClass("PRESENCE_IN",PresenceInEvent.class);
		
		
    }
	
	
	
	@SuppressWarnings("unchecked")
	public  InboundEvent buildEvent(Map<String, String> attributes) {
		InboundEvent event=null;
		try {
			String  eventName = attributes.get("Event-Name");
			if(eventName==null){
				logger.error("Couldn't found the Event-Name");
				return null;
			}
			if("CUSTOM".equals(eventName)){
				final String eventSubclass = attributes.get("Event-Subclass");
				if(eventSubclass==null){
					logger.error("Couldn't found the Event-Subclass in CUSTOM Event");
					return null;
				}
				
				eventName = eventName+':'+eventSubclass;
			}
			final Class<? extends InboundEvent> eventClass =  registeredEventClasses.get(eventName);
			if(eventClass==null) return null;
			event = eventClass.newInstance();
			
			
			for(Map.Entry<String, String> entry:attributes.entrySet()){
				final String attributeKey = entry.getKey();
				if(!attributeKey.equals("Event-Name")){
					final String attributeValue = entry.getValue();
					if(attributeKey.startsWith("variable_")){
						if(upperCasePropertyies.get(eventName+"VARIABLES")!=null){
							final String mapKey = StringUtils.substringAfter(attributeKey, "_");
							PropertyUtils.setMappedProperty(event, "variables", mapKey, attributeValue);
						}
					}else{
						final String propertyName = upperCasePropertyies.get(eventName+attributeKey.replaceAll("-", "").toUpperCase());
						if(propertyName!=null){
							BeanUtils.setProperty(event, propertyName, attributeValue);
						}else{
//							logger.debug("Ignore event:"+eventName+" propertyName:"+attributeKey);
						}
					}
				}
			}
			
		} catch (InstantiationException e) {
			e.printStackTrace();
			logger.error("The error is {}",e);
		} catch (IllegalAccessException e) {
			logger.error("The error is {}",e);
			e.printStackTrace();
		}
		catch (InvocationTargetException e) {
			e.printStackTrace();
			logger.error("The error is {}",e);
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
			logger.error("The error is {}",e);
		}

        return event;
	}

}
